"""
日志处理工具类，提供日志打印和格式化功能
"""

import logging
import os
import sys
import threading
from typing import Optional, Union
from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler
import time
from datetime import datetime


class LogUtils:
    """
    日志处理工具类，提供各种日志操作功能
    """
    
    # 日志级别映射
    LEVELS = {
        'DEBUG': logging.DEBUG,
        'INFO': logging.INFO,
        'WARNING': logging.WARNING,
        'ERROR': logging.ERROR,
        'CRITICAL': logging.CRITICAL
    }
    
    # 用于存储已锁定的文件
    _locked_files = {}
    _lock = threading.Lock()
    
    def __init__(self, 
                 name: str = 'default_logger',
                 level: str = 'INFO',
                 log_format: str = None,
                 date_format: str = None):
        """
        初始化日志处理工具

        :param name: 日志记录器名称
        :param level: 日志级别
        :param log_format: 日志格式
        :param date_format: 日期格式
        """
        self.name = name
        self.level = level
        
        # 默认日志格式
        if log_format is None:
            self.log_format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
        else:
            self.log_format = log_format
            
        # 默认日期格式
        if date_format is None:
            self.date_format = '%Y-%m-%d %H:%M:%S'
        else:
            self.date_format = date_format
        
        # 创建日志记录器
        self.logger = logging.getLogger(name)
        self.logger.setLevel(self.LEVELS.get(level.upper(), logging.INFO))
        
        # 清除已有的处理器，避免重复
        self.logger.handlers.clear()
        
        # 创建格式化器
        self.formatter = logging.Formatter(self.log_format, self.date_format)
    
    def add_console_handler(self, level: str = None) -> 'LogUtils':
        """
        添加控制台处理器

        :param level: 日志级别
        :return: LogUtils实例（支持链式调用）
        """
        if level is None:
            level = self.level
            
        console_handler = logging.StreamHandler(sys.stdout)
        console_handler.setLevel(self.LEVELS.get(level.upper(), logging.INFO))
        console_handler.setFormatter(self.formatter)
        self.logger.addHandler(console_handler)
        return self
    
    def add_file_handler(self, 
                         file_path: str, 
                         level: str = None, 
                         max_bytes: int = 10485760, 
                         backup_count: int = 5) -> 'LogUtils':
        """
        添加文件处理器（支持轮转）

        :param file_path: 日志文件路径
        :param level: 日志级别
        :param max_bytes: 单个日志文件最大字节数
        :param backup_count: 保留的备份文件数量
        :return: LogUtils实例（支持链式调用）
        """
        if level is None:
            level = self.level
            
        # 确保目录存在
        directory = os.path.dirname(file_path)
        if directory and not os.path.exists(directory):
            os.makedirs(directory)
        
        # 处理文件锁定问题
        unique_file_path = self._get_unique_file_path(file_path)
        
        file_handler = RotatingFileHandler(
            unique_file_path, 
            maxBytes=max_bytes, 
            backupCount=backup_count, 
            encoding='utf-8'
        )
        file_handler.setLevel(self.LEVELS.get(level.upper(), logging.INFO))
        file_handler.setFormatter(self.formatter)
        self.logger.addHandler(file_handler)
        return self
    
    def add_timed_rotating_file_handler(self,
                                       file_path: str,
                                       when: str = 'midnight',
                                       interval: int = 1,
                                       backup_count: int = 30,
                                       level: str = None) -> 'LogUtils':
        """
        添加时间轮转文件处理器

        :param file_path: 日志文件路径
        :param when: 轮转时机 ('S'秒, 'M'分, 'H'小时, 'D'天, 'midnight'午夜, 'W0'-'W6'周一到周日)
        :param interval: 轮转间隔
        :param backup_count: 保留的备份文件数量
        :param level: 日志级别
        :return: LogUtils实例（支持链式调用）
        """
        if level is None:
            level = self.level
            
        # 确保目录存在
        directory = os.path.dirname(file_path)
        if directory and not os.path.exists(directory):
            os.makedirs(directory)
        
        # 处理文件锁定问题
        unique_file_path = self._get_unique_file_path(file_path)
        
        timed_handler = TimedRotatingFileHandler(
            unique_file_path,
            when=when,
            interval=interval,
            backupCount=backup_count,
            encoding='utf-8'
        )
        timed_handler.setLevel(self.LEVELS.get(level.upper(), logging.INFO))
        timed_handler.setFormatter(self.formatter)
        self.logger.addHandler(timed_handler)
        return self
    
    def _get_unique_file_path(self, file_path: str) -> str:
        """
        获取唯一的文件路径，避免多实例运行时的文件冲突

        :param file_path: 原始文件路径
        :return: 唯一的文件路径
        """
        # 如果文件不存在锁定问题，直接返回原路径
        if not os.path.exists(file_path):
            return file_path
            
        # 检查文件是否已被当前进程锁定
        with LogUtils._lock:
            if file_path in LogUtils._locked_files:
                # 如果已被当前进程锁定，返回原路径
                if LogUtils._locked_files[file_path] == os.getpid():
                    return file_path
                # 如果被其他进程锁定，生成新文件名
                else:
                    return self._generate_unique_filename(file_path)
            else:
                # 尝试锁定文件
                try:
                    # 尝试以独占模式打开文件
                    with open(file_path, 'a+', encoding='utf-8') as f:
                        # 如果成功，记录锁定信息
                        LogUtils._locked_files[file_path] = os.getpid()
                        return file_path
                except IOError:
                    # 如果失败，生成新文件名
                    return self._generate_unique_filename(file_path)
    
    def _generate_unique_filename(self, file_path: str) -> str:
        """
        生成唯一的文件名

        :param file_path: 原始文件路径
        :return: 带时间戳和进程ID的唯一文件路径
        """
        # 分离文件名和扩展名
        base_name, ext = os.path.splitext(file_path)
        
        # 添加时间戳和进程ID
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        pid = os.getpid()
        
        unique_file_path = f"{base_name}_{timestamp}_{pid}{ext}"
        return unique_file_path
    
    def set_level(self, level: str) -> 'LogUtils':
        """
        设置日志级别

        :param level: 日志级别
        :return: LogUtils实例（支持链式调用）
        """
        self.level = level
        self.logger.setLevel(self.LEVELS.get(level.upper(), logging.INFO))
        return self
    
    def set_format(self, log_format: str, date_format: str = None) -> 'LogUtils':
        """
        设置日志格式

        :param log_format: 日志格式
        :param date_format: 日期格式
        :return: LogUtils实例（支持链式调用）
        """
        self.log_format = log_format
        if date_format is not None:
            self.date_format = date_format
            
        self.formatter = logging.Formatter(self.log_format, self.date_format)
        
        # 更新所有处理器的格式化器
        for handler in self.logger.handlers:
            handler.setFormatter(self.formatter)
            
        return self
    
    def debug(self, message: str, *args, **kwargs) -> None:
        """
        记录DEBUG级别日志

        :param message: 日志消息
        """
        self.logger.debug(message, *args, **kwargs)
    
    def info(self, message: str, *args, **kwargs) -> None:
        """
        记录INFO级别日志

        :param message: 日志消息
        """
        self.logger.info(message, *args, **kwargs)
    
    def warning(self, message: str, *args, **kwargs) -> None:
        """
        记录WARNING级别日志

        :param message: 日志消息
        """
        self.logger.warning(message, *args, **kwargs)
    
    def error(self, message: str, *args, **kwargs) -> None:
        """
        记录ERROR级别日志

        :param message: 日志消息
        """
        self.logger.error(message, *args, **kwargs)
    
    def critical(self, message: str, *args, **kwargs) -> None:
        """
        记录CRITICAL级别日志

        :param message: 日志消息
        """
        self.logger.critical(message, *args, **kwargs)
    
    def exception(self, message: str, *args, **kwargs) -> None:
        """
        记录异常日志（ERROR级别）

        :param message: 日志消息
        """
        self.logger.exception(message, *args, **kwargs)
    
    def log(self, level: str, message: str, *args, **kwargs) -> None:
        """
        记录指定级别的日志

        :param level: 日志级别
        :param message: 日志消息
        """
        log_level = self.LEVELS.get(level.upper(), logging.INFO)
        self.logger.log(log_level, message, *args, **kwargs)
    
    @staticmethod
    def get_logger(name: str = 'default_logger',
                   level: str = 'INFO',
                   log_format: str = None,
                   date_format: str = None) -> 'LogUtils':
        """
        获取日志记录器实例

        :param name: 日志记录器名称
        :param level: 日志级别
        :param log_format: 日志格式
        :param date_format: 日期格式
        :return: LogUtils实例
        """
        return LogUtils(name, level, log_format, date_format)
    
    @staticmethod
    def create_simple_logger(name: str = 'simple_logger') -> 'LogUtils':
        """
        创建简单配置的日志记录器（控制台输出）

        :param name: 日志记录器名称
        :return: LogUtils实例
        """
        return LogUtils(name).add_console_handler()
    
    @staticmethod
    def create_file_logger(name: str = 'file_logger',
                          file_path: str = 'app.log') -> 'LogUtils':
        """
        创建文件日志记录器

        :param name: 日志记录器名称
        :param file_path: 日志文件路径
        :return: LogUtils实例
        """
        return LogUtils(name).add_file_handler(file_path).add_console_handler()
    
    @staticmethod
    def create_unique_file_logger(name: str = 'unique_file_logger',
                                 file_path: str = 'app.log') -> 'LogUtils':
        """
        创建唯一文件名的日志记录器，避免多实例冲突

        :param name: 日志记录器名称
        :param file_path: 日志文件路径
        :return: LogUtils实例
        """
        logger = LogUtils(name)
        # 使用带时间戳的唯一文件名
        base_name, ext = os.path.splitext(file_path)
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        pid = os.getpid()
        unique_file_path = f"{base_name}_{timestamp}_{pid}{ext}"
        return logger.add_file_handler(unique_file_path).add_console_handler()